package org.springside.modules.web.struts2;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.beanutils.MethodUtils;
import org.apache.struts2.ServletActionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

/**
 * CRUD的Action基类. 用户项目中的CRUD方法可能会有不同,可进行扩展或重新编写CRUDAction和CRUDInvoker.
 * 
 * @param <T>
 *            CRUD对象的类型
 * 
 * @see Preparable
 * @see ModelDriven
 * 
 * @author calvin
 */
@SuppressWarnings("unchecked")
public abstract class CRUDAction<T> extends ActionSupport implements
		Preparable, ModelDriven {

	// list页面定义
	public static final String LIST = "list";

	// logger for subclass
	protected final Logger logger = LoggerFactory.getLogger(getClass());

	// the model
	protected T entity;

	protected String msg;

	protected int pageIndex; 
 
	protected int pageSize;
	
	protected Long pageCount;
	// model's Class
	protected Class<T> entityClass;

	// model's ClassName
	protected String entityClassName;

	// manager's CRUD method invoker
	protected CRUDInvoker invoker;

	// list页面显示的对象列表
	protected List<T> entites;

	public CRUDAction() {
		super();
		// 通过反射取得Entity的Class.
		this.entityClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		entityClassName = entityClass.getSimpleName();
	}

	/**
	 * @see ModelDriven
	 */
	public T getModel() {
		return entity;
	}

	/**
	 * @see Preparable
	 */
	public void prepare() throws Exception {
		if (getId() != null) {
			entity = (T) MethodUtils.invokeMethod(getManager(), "get"
					+ entityClassName, getId());
			if (entity == null)
				throw new IllegalArgumentException("id not exist");
		} else
			entity = entityClass.newInstance();
		onPrepare();
	}

	/**
	 * 显示对象列表页.
	 */
	public String list() throws Exception { 
		onDisplayPrepare();
		entites = getInvoker().getAll(getManager(), entityClassName,
				new int[] { pageIndex, pageSize });
		return LIST;
	}

	/**
	 * 显示新增或修改的表单.
	 */
	@Override
	public String input() throws Exception {
		onDisplayPrepare();
		return INPUT;
	}

	/**
	 * 新增或修改对象保存.
	 */
	public String save() throws Exception {
		if (getId() == null)
			getInvoker().create(getManager(), entityClass, entity);
		else
			getInvoker().update(getManager(), entityClass, entity);
		return SUCCESS;
	}

	/**
	 * 删除对象.
	 */
	public String delete() throws Exception {
		getInvoker().delete(getManager(), entityClassName, getId());
		return SUCCESS;
	}

	/**
	 * 默认执行函数,显示对象列表.
	 */
	@Override
	public String execute() throws Exception {
		return list();
	}

	/**
	 * 向显示页面返回对象列表.
	 */
	public List<T> getEntites() {
		return entites;
	}

	/**
	 * 取得Manager的CRUD方法反射调用者. 用户可重载此方法注入自己的Invoker.
	 */
	protected CRUDInvoker getInvoker() {
		if (invoker == null)
			invoker = new DefaultCRUDInvoker();
		return invoker;
	}

	/**
	 * 取得管理Manager,子类必须实现.
	 */
	protected abstract Object getManager();

	/**
	 * 从参数中取得id,子类必须实现.
	 */
	protected abstract Object getId();

	/**
	 * @see #prepare()
	 */
	protected void onPrepare() {
		// empty,可在子类重载,在prepare()中调用.
	}

	/**
	 * @see #list()
	 * @see #input()
	 */
	protected void onDisplayPrepare() {
		// empty,可在子类重载.准备显示页面需要的辅助对象,在list()、input()中调用.
	}

	/**
	 * Convenience method to get the request
	 * 
	 * @return current request
	 */
	public HttpServletRequest getRequest() {
		return ServletActionContext.getRequest();
	}

	public HttpServletResponse getResponse() {
		return ServletActionContext.getResponse();
	}

	/**
	 * Convenience method to get the session
	 */
	public HttpSession getSession() {
		return getRequest().getSession();
	}

	// 异常信息
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public int getPageIndex() {
		return pageIndex;
	}

	public void setPageIndex(int pageIndex) {
		this.pageIndex = pageIndex;
	}

	public Long getPageCount() {
		return pageCount;
	}

	public void setPageCount(Long pageCount) {
		this.pageCount = pageCount;
	}
 
}
